package com.googlecode.imanager.client;

import java.io.Serializable;
import java.util.List;

import com.googlecode.imanager.client.bridge.IContainer;
import com.googlecode.imanager.client.event.PhaseEvent;
import com.googlecode.imanager.client.exception.ValidationException;
import com.googlecode.imanager.client.form.HasValidation;
import com.googlecode.imanager.client.layout.LayoutManager;
import com.googlecode.imanager.client.util.Dimension;
import com.googlecode.imanager.client.util.UniqueList;
import com.googlecode.imanager.client.util.Utils;

/**
 * @author wellsantos@wellsantos.com
 * @since 0.01
 */
public abstract class Container extends Control implements IContainer {

    private static final long serialVersionUID = 1L;

    private int insetLeft;
    private int insetTop;
    private int insetRight;
    private int insetBottom;
    private LayoutManager layout;
    private List<Control> children;
    private Control header;
    private Control footer;

    public Container(){
        this(100, 100);
    }
    
    protected Container(int width, int height) {
        super(width, height);
        children = createChildList();
    }
    
    ChildList<Control> createChildList(){
        return new ChildList<Control>();
    }

    @Override
    protected void onResized(Dimension clientSize){
        super.onResized(clientSize);
        for (Control control : getChildren()){
            control.onParentResized(clientSize);
        }
        if (layout != null){
            layout.layoutOnResized(this, clientSize);
        }
    }
    
    @Override
    protected void onPhaseEvent(PhaseEvent e) {
        super.onPhaseEvent(e);
        for (Control child : getChildren()){
            child.onPhaseEvent(e);
        }
    }

    protected void onChildAdded(Control child){
        child.onParentResized(child.getParentSize());
    }
    
    protected void onChildRemoved(Control child){
    }
    
    protected void onChildResized(Control child, Dimension clientSize){
    }
    
    protected void onChildPositionChanged(Control child){
    }
    
    public final void add(Control control){
        getChildren().add(control);
    }
    
    public final void remove(Control control){
        getChildren().remove(control);
    }
    
    public final List<Control> getChildren() {
        return children;
    }
    
    @Override
    public final int getInsetLeft(){
        return insetLeft;
    }
    
    @Override
    public final int getInsetTop(){
        return insetTop;
    }
    
    @Override
    public final int getInsetRight(){
        return insetRight;
    }
    
    @Override
    public final int getInsetBottom(){
        return insetBottom;
    }
    
    void setInsets(int insetLeft, int insetTop, int insetRight, int insetBottom){
        
        if ((this.insetLeft + this.insetRight != insetLeft + insetRight)
                || (this.insetTop + this.insetBottom != insetTop + insetBottom)){

            this.insetLeft = insetLeft;
            this.insetTop = insetTop;
            this.insetRight = insetRight;
            this.insetBottom = insetBottom;
            onResized(getClientSize());
        }
    }
    
    public LayoutManager getLayout() {
        return layout;
    }

    public void setLayout(LayoutManager layout) {
        if (!Utils.isEquals(this.layout, layout)){
            this.layout = layout;
            doLayoutChildren();
        }
    }

    public void validateChildren() throws ValidationException {
        for (Control control : getChildren()){
            if (control instanceof HasValidation){
                ((HasValidation) control).validate();
            }
        }
    }
    
    protected void doLayoutChild(Control child, int flags){
        LayoutManager layout = getLayout();        
        if (layout != null){
            layout.layoutChild(child, flags);
        }
    }

    protected void doLayoutChildren() {
        LayoutManager layout = getLayout();
        if (layout != null){
            layout.layoutChildren(this);
        }
    }
    
    @Override
    public void realign(){
        doLayoutChildren();
        super.realign();
    }
    
    public void scrollToChild(Control child){
        if (child != null){
            scrollToRectVisible(child.getLeft(), child.getTop(), child.getWidth(), child.getHeight());
        }
    }

    public Control getHeader() {
        return header;
    }

    public void setHeader(Control header) {
        this.header = header;
    }

    public Control getFooter() {
        return footer;
    }

    public void setFooter(Control footer) {
        this.footer = footer;
    }

    @Override
    protected IContainer getBridge() {
        return (IContainer) lookupBridge();
    }
    
    @Override
    public Container clone() {
        Container c = (Container) super.clone();
        c.children = c.new ChildList<Control>();
        return c;
    }
    
    @Override
    public Container cloneTree() {
        Container c = clone();

        for (Control child : getChildren()){
            child.cloneTree().setParent(c);
        }

        return c;
    }


    /**
     * @author wellsantos@wellsantos.com
     * @since 0.01
     */
    class ChildList<T extends Control> extends UniqueList<T> implements Serializable {

        private static final long serialVersionUID = 1L;

        public ChildList() {
            super();
        }

        @Override
        protected void onAfterAdd(int index, T item) {
            item.setParent(Container.this);
        }

        @Override
        protected void onAfterRemove(int index, T item) {
            item.setParent(null);
        }

        @Override
        protected void onAfterReplace(int index, T oldItem, T newItem) {
            onAfterAdd(index, newItem);
        }

        @Override
        protected void onBeforeReplace(int index, T oldItem, T newItem) {
            onBeforeRemove(index, oldItem);
            onBeforeAdd(index, newItem);
            onAfterRemove(index, oldItem);
        }
        
    }
    
}
